import * as vscode from 'vscode';
import { disposeAll } from './dispose';
import { isPomlFile } from '../../poml-vscode/util/file';

export class HTMLFileTopmostLineMonitor {
  private readonly disposables: vscode.Disposable[] = [];

  private readonly pendingUpdates = new Map<string, number>();

  private readonly throttle = 50;

  constructor() {
    vscode.window.onDidChangeTextEditorVisibleRanges(
      (event) => {
        if (isPomlFile(event.textEditor.document)) {
          const line = getVisibleLine(event.textEditor);
          if (typeof line === 'number') {
            this.updateLine(event.textEditor.document.uri, line);
          }
        }
      },
      null,
      this.disposables,
    );
  }

  dispose() {
    disposeAll(this.disposables);
  }

  private readonly _onDidChangeTopmostLineEmitter = new vscode.EventEmitter<{ resource: vscode.Uri; line: number }>();
  public readonly onDidChangeTopmostLine = this._onDidChangeTopmostLineEmitter.event;

  private updateLine(resource: vscode.Uri, line: number) {
    const key = resource.toString();
    if (!this.pendingUpdates.has(key)) {
      // schedule update
      setTimeout(() => {
        if (this.pendingUpdates.has(key)) {
          this._onDidChangeTopmostLineEmitter.fire({
            resource,
            line: this.pendingUpdates.get(key) as number,
          });
          this.pendingUpdates.delete(key);
        }
      }, this.throttle);
    }

    this.pendingUpdates.set(key, line);
  }
}

/**
 * Get the top-most visible range of `editor`.
 *
 * Returns a fractional line number based the visible character within the line.
 * Floor to get real line number
 */
export function getVisibleLine(editor: vscode.TextEditor): number | undefined {
  if (!editor.visibleRanges.length) {
    return undefined;
  }

  const firstVisiblePosition = editor.visibleRanges[0].start;
  const lineNumber = firstVisiblePosition.line;
  const line = editor.document.lineAt(lineNumber);
  const progress = firstVisiblePosition.character / (line.text.length + 2);
  return lineNumber + progress;
}
